/*
 * AhaFirmware.cpp
 *
 *  Created on: 25 mars 2011
 *      Author: eric
 */

#include "AhaFirmware.h"

void AhaFirmware::init() {
	uint8_t nodeLoaded;
	nodeState = AHA_STATE_STARTUP;

	// read nodeID
	nodeID[0] = 0x77;
	nodeID[1] = 0x77;
	nodeID[2] = 0x77;
	nodeID[3] = 0x77;
	nodeID[4] = 0x77;
	nodeID[5] = 0x77;
	// lecture EEPROM
	eeprom_read_block(&nodeLoaded, (const void*) AHA_NODE_LOADED_ADDRESS,
			AHA_NODE_LOADED_SIZE_BLOCK);
	if (nodeLoaded != AHA_EEPROM_LOADED) {
		// The node EEPROM is empty
		nodeState = AHA_STATE_NEED_ID;
	} else {
		// read the configuration
		nodeState = AHA_STATE_ACTIVE;
		eeprom_read_block(&nodeAdr, (const void*) AHA_NODE_ADDRESS_ADDRESS,
				AHA_NODE_ADDRESS_SIZE_BLOCK);
		eeprom_read_block(&nodeConfigVersion,
				(const void*) AHA_NODE_CFG_VERSION_ADDRESS,
				AHA_NODE_CFG_VERSION_SIZE_BLOCK);
		// loop for binding data
		for (uint8_t i = 0; i < AHA_MAX_BINDING; i++) {
			eeprom_read_block(&Binding[i],
					(const void*) (AHA_NODE_BINDING_ADDRESS + i
							* AHA_NODE_BINDING_SIZE_BLOCK),
					AHA_NODE_BINDING_SIZE_BLOCK);
		}
	}
	// NVi and NVo initialisation
	// Bouton poussoir           Binding     Lampe
	//                              1
	// nviBoutonPoussoirSet(0)   <------     nvoLampeStatus(3)
	//                              2
	// nvoBoutonPoussoirStatus(1)------>     nviLampeSet(2)
	// Button
	NVar[NVI_BOUTON_POUSSOIR_SET].NVDir = AHA_NV_DIR_INPUT;
	NVar[NVI_BOUTON_POUSSOIR_SET].NVType = AHA_NV_TYPE_SWITCH;
	NVar[NVI_BOUTON_POUSSOIR_SET].data = malloc(sizeof(AhaTypes::AHA_switch));
	NVar[NVO_BOUTON_POUSSOIR_STATUS].NVDir = AHA_NV_DIR_OUTPUT;
	NVar[NVO_BOUTON_POUSSOIR_STATUS].NVType = AHA_NV_TYPE_SWITCH;
	NVar[NVO_BOUTON_POUSSOIR_STATUS].data = NVar[NVI_BOUTON_POUSSOIR_SET].data;
	pinMode(CARD_BUTTON_PIN, INPUT);
	// LED
	NVar[NVI_LAMPE_SET].NVDir = AHA_NV_DIR_INPUT;
	NVar[NVI_LAMPE_SET].NVType = AHA_NV_TYPE_SWITCH;
	NVar[NVI_LAMPE_SET].data = malloc(sizeof(AhaTypes::AHA_switch));
	NVar[NVO_LAMPE_STATUS].NVDir = AHA_NV_DIR_OUTPUT;
	NVar[NVO_LAMPE_STATUS].NVType = AHA_NV_TYPE_SWITCH;
	NVar[NVO_LAMPE_STATUS].data = NVar[NVI_LAMPE_SET].data;
	pinMode(CARD_LIGHT_PIN, OUTPUT);
	digitalWrite(CARD_LIGHT_PIN, LOW);

	buttonState = 0; // current state of the button
	lastButtonState = 0; //
	resetTimer();
}

void AhaFirmware::saveCfg() {
	uint8_t nodeLoaded = AHA_EEPROM_LOADED;
	eeprom_write_block(&nodeLoaded, (void *) AHA_NODE_LOADED_ADDRESS,
			AHA_NODE_LOADED_SIZE_BLOCK);
	eeprom_write_block(&nodeAdr, (void *) AHA_NODE_ADDRESS_ADDRESS,
			AHA_NODE_ADDRESS_SIZE_BLOCK);
	eeprom_read_block(&nodeConfigVersion,
			(const void*) AHA_NODE_CFG_VERSION_ADDRESS,
			AHA_NODE_CFG_VERSION_SIZE_BLOCK);
	// loop for binding data
	for (uint8_t i = 0; i < AHA_MAX_BINDING; i++) {
		eeprom_write_block(&Binding[i], (void *) (AHA_NODE_BINDING_ADDRESS + i
				* AHA_NODE_BINDING_SIZE_BLOCK), AHA_NODE_BINDING_SIZE_BLOCK);
	}
}

void AhaFirmware::resetTimer() {
	previousTime = 0;
}

void AhaFirmware::sendMessage(AhaProtocol::AHA_MSG *ahaMsg) {
	Can::CAN_MSG canMsg;
	AhaProtocol ahaProtocol;

	ahaProtocol.setCanMessage(&canMsg, ahaMsg);
	toCanQueue.push(canMsg);
	fromCanQueue.push(canMsg);
}

void AhaFirmware::process() {
	AhaProtocol::AHA_MSG outputMsg;

	if (nodeState == AHA_STATE_ACTIVE) {
		// normal process process Output Variables
		// read the pushbutton input pin:
		buttonState = (int8_t) digitalRead(CARD_BUTTON_PIN);

		// compare the buttonState to its previous state
		if (buttonState != lastButtonState) {
			// if the state has changed, increment the counter
			if (buttonState == HIGH) {
				// if the current state is HIGH then the button
				// come from off to on:
				NVar[NVO_BOUTON_POUSSOIR_STATUS].dSwitch->state
						= NVar[NVO_BOUTON_POUSSOIR_STATUS].dSwitch->state
								== AHA_TYP_SWITCH_ON ? AHA_TYP_SWITCH_OFF
								: AHA_TYP_SWITCH_ON;
				NVar[NVO_BOUTON_POUSSOIR_STATUS].dSwitch->value
						= NVar[NVO_BOUTON_POUSSOIR_STATUS].dSwitch->value
								== AHA_TYP_SWITCH_ON ? AHA_TYP_SWITCH_OFF
								: AHA_TYP_SWITCH_ON;
				for (uint8_t i = 0; i < AHA_MAX_BINDING; i++) {
					if (Binding[i].NVID == NVO_BOUTON_POUSSOIR_STATUS) {
						ahaTypes.setCanMessage(&outputMsg, nodeAdr,
								(uint8_t) CID_DIR_OUTPUT,
								Binding[i].NVBindingId,
								NVar[NVO_BOUTON_POUSSOIR_STATUS].dSwitch);
						sendMessage(&outputMsg);
					}
				}
			}
		}
		// save the current state as the last state,
		//for next time through the loop
		lastButtonState = buttonState;
	} else if (nodeState == AHA_STATE_NEED_ID) {
		// send message for node ID
		if (previousTime == 0) {
			// first request
			ahaProtocol.sendEMNeedId(&outputMsg, nodeID);
			sendMessage(&outputMsg);
			previousTime = millis();
		} else if ((millis() - previousTime) >= 5000) {
			// other request every 5s
			ahaProtocol.sendEMNeedId(&outputMsg, nodeID);
			sendMessage(&outputMsg);
			previousTime = millis();
		}
	} else if (nodeState == AHA_STATE_NEED_CFG) {
		// send message for config
		if (previousTime == 0) {
			// first request
			ahaProtocol.sendEMNeedCfg(&outputMsg, nodeAdr);
			sendMessage(&outputMsg);
			previousTime = millis();
		} else if ((millis() - previousTime) >= 5000) {
			// other request every 5s
			ahaProtocol.sendEMNeedCfg(&outputMsg, nodeAdr);
			sendMessage(&outputMsg);
			previousTime = millis();
		}
	} else {
		// error or waiting CFG
	}
}

uint8_t AhaFirmware::processInputMessage(Can::CAN_MSG *canMsg) {
	AhaProtocol::AHA_MSG inputMsg;
	AhaProtocol::AHA_MSG outputMsg;
	ahaProtocol.getAhaMessage(canMsg, &inputMsg);
	if (inputMsg.CidTyp == CID_TYP_NV) {
		// NV binding message
		if (nodeState == AHA_STATE_ACTIVE) {
			// card is initialized
			for (uint8_t i = 0; i < AHA_MAX_BINDING; i++) {
				if ((Binding[i].NVBindingId == inputMsg.CidNvBnd)
						&& (NVar[Binding[i].NVID].NVDir == CID_DIR_INPUT)) {
					switch (Binding[i].NVID) {
					case NVI_BOUTON_POUSSOIR_SET:
						// change value of the button (sensor)
						NVar[NVI_BOUTON_POUSSOIR_SET].dSwitch->state
								= (uint8_t) inputMsg.data[0];
						NVar[NVI_BOUTON_POUSSOIR_SET].dSwitch->value
								= (uint8_t) inputMsg.data[1];
						break;
					case NVI_LAMPE_SET:
						// change value of the light (actuator)
						NVar[NVI_LAMPE_SET].dSwitch->state
								= (uint8_t) inputMsg.data[0];
						NVar[NVI_LAMPE_SET].dSwitch->value
								= (uint8_t) inputMsg.data[1];
						digitalWrite(CARD_LIGHT_PIN,
								NVar[NVI_LAMPE_SET].dSwitch->value);
						// send new status of the light
						for (uint8_t j = 0; j < AHA_MAX_BINDING; j++) {
							if (Binding[j].NVID == NVO_LAMPE_STATUS) {
								ahaTypes.setCanMessage(&outputMsg, nodeAdr,
										(uint8_t) CID_DIR_OUTPUT,
										Binding[j].NVBindingId,
										NVar[NVO_LAMPE_STATUS].dSwitch);
								sendMessage(&outputMsg);
							}
						}
						break;
					}
				}
			}
		}
	} else {
		// Explicit message
		if (inputMsg.CidNodeDst == nodeAdr || inputMsg.CidNodeDst
				== AhaProtocol::BROADCAST_NODE) {
			// it s for this node
			switch (inputMsg.CidEm) {
			case CID_EM_SET_ID:
				if (inputMsg.nbytes > 7) {
					if (inputMsg.data[0] == nodeID[0] && inputMsg.data[1]
							== nodeID[1] && inputMsg.data[2] == nodeID[2]
							&& inputMsg.data[3] == nodeID[3]
							&& inputMsg.data[4] == nodeID[4]
							&& inputMsg.data[5] == nodeID[5]) {
						// we have the node id
						nodeAdr = (uint16_t) (inputMsg.data[6]);
						nodeAdr
								+= ((uint16_t) (inputMsg.data[7] & CID_3b_MASK))
										<< 8;
						nodeState = AHA_STATE_NEED_CFG;
						resetTimer();
					}
				}
				break;
			case CID_EM_START_CFG:
				if (inputMsg.nbytes > 0) {
					uint8_t val = (uint8_t) (inputMsg.data[0]);
					nodeState = AHA_STATE_INIT;
					for (uint8_t i = 0; i < AHA_MAX_BINDING; i++) {
						if (i < val) {
							Binding[i].NVID = AHA_NVID_NOT_SET;
							Binding[i].NVBindingId = 0;
						} else {
							Binding[i].NVID = AHA_NVID_NULL;
							Binding[i].NVBindingId = 0;
						}
					}
				}
				break;
			case CID_EM_SET_CFG:
				if (inputMsg.nbytes > 3) {
					uint8_t val = (uint8_t) (inputMsg.data[0]);
					Binding[val].NVID = inputMsg.data[1];
					Binding[val].NVBindingId = (uint16_t) (inputMsg.data[2]);
					Binding[val].NVBindingId += ((uint16_t) (inputMsg.data[3]
							& CID_7b_MASK)) << 8;
				}
				break;
			case CID_EM_END_CFG:
				if (inputMsg.nbytes > 1) {
					nodeState = AHA_STATE_ACTIVE;
					for (uint8_t i = 0; i < AHA_MAX_BINDING; i++) {
						if (Binding[i].NVID == AHA_NVID_NOT_SET)
							nodeState = AHA_STATE_NEED_CFG;
					}
					if (nodeState == AHA_STATE_ACTIVE) {
						nodeConfigVersion = (uint16_t) (inputMsg.data[0]);
						nodeConfigVersion += ((uint16_t) (inputMsg.data[1]))
								<< 8;
						// save the configuration
						saveCfg();
					}
				}
				break;
			}
		}
	}
	return 0;
}

uint8_t AhaFirmware::getMajorVersion(void) {
	return AHA_MAJOR_VERSION;
}

uint8_t AhaFirmware::getMinorVersion(void) {
	return AHA_MINOR_VERSION;
}

uint8_t AhaFirmware::getSubMinorVersion(void) {
	return AHA_SUB_VERSION;
}
